﻿//**********************************************************
//
//**********************************************************
function Epg(maxNumberOfChannelsPerScreen, timeLinePeriod, timeLineStep, minNoDataPeriod)
{
	var _this = this;
	var _mediaController = osh.mediaController;
	var _libraryNavigator;
	var _treeNavigator;

	var _timeLinePeriod = timeLinePeriod;
	var _timeLineStep = timeLineStep;
	var _minNoDataPeriod = minNoDataPeriod;

	var _channels = []; // The array of channels currently shown on the screen.
	var _numberOfChannels = 0; // Actual number of channels per screen (equals to _channels.length).
	var _totalNumberOfChannels = 0; // Total number of channel in the tree.

	var _firstChannelIndex = -1;
	var _currentChannelIndex = 0;
	var _currentEventIndex = 0;

	var _startTime = null;
	var _endTime = null;

	// Time used to select the right event when navigating up and down the grid.
	// Usualy it is current time or the middle of the event from which the navigation up/down is started.
	var _upDownNavigationTime = null;

	this.onPageChange = new Event();
	this.onEventChange = new Event();


	function initialize()
	{
		_this.updateMediaTree();
	}

	this.getVisibleChannels = function()
	{
		return _channels;
	}

	this.getStartTime = function()
	{
		return _startTime;
	}

	this.getEndTime = function()
	{
		return _endTime;
	}

	this.isEmpty = function()
	{
		return _totalNumberOfChannels == 0;
	}

	this.playCurrentMedia = function()
	{
		if(!_treeNavigator) return;

		var mediaPosition = _firstChannelIndex + _currentChannelIndex;
		var treeNavigator = _treeNavigator.Clone();
		if(treeNavigator.MoveToFirstMedia())
			if(mediaPosition > 0)
			if(!treeNavigator.MoveToMedia(mediaPosition))
			treeNavigator.MoveToLastMedia();

		_mediaController.getMediaLibraryNavigator().MoveTo(_libraryNavigator);

		var currentTreeNavigator = _mediaController.getCurrentTreeNavigator();
		if(currentTreeNavigator)
			if(currentTreeNavigator.MoveTo(treeNavigator))
			_mediaController.playCurrentMedia();

	}

	this.getCurrentChannel = function()
	{
		if(_currentChannelIndex >= 0 && _currentChannelIndex < _channels.length)
			return _channels[_currentChannelIndex];
		return null;
	}

	this.getCurrentEvent = function()
	{
		var channelInfo = this.getCurrentChannel();
		if(!channelInfo) return null;

		if(_currentEventIndex >= 0 && _currentEventIndex < channelInfo.events.length)
			return channelInfo.events[_currentEventIndex];

		return null;
	}

	this.setCurrentEvent = function(eventId)
	{
		var lastEvent = this.getCurrentEvent();
		for(var c = 0; c < _channels.length; c++)
		{
			var events = _channels[c].events;
			for(var e = 0; e < events.length; e++)
			{
				var currentEvent = events[e];
				if(currentEvent.id == eventId)
				{
					_currentChannelIndex = c;
					_currentEventIndex = e;
					updateUpDownNavigationTime(currentEvent);
					fireCurrentEventChange(lastEvent, currentEvent);
					return;
				}
			}
		}
		_currentChannelIndex = -1;
		_currentEventIndex = -1;
	}

	this.updateMediaTree = function(libraryNavigator)
	{
		_firstChannelIndex = -1;
		_currentChannelIndex = 0;
		_currentEventIndex = 0;
		_channels = [];
		_libraryNavigator = (libraryNavigator || _mediaController.getMediaLibraryNavigator()).Clone();
		_treeNavigator = _libraryNavigator.CurrentTreeNavigator;
		_totalNumberOfChannels = getTotalNumberOfChannels(_treeNavigator);
		_numberOfChannels = Math.min(maxNumberOfChannelsPerScreen, _totalNumberOfChannels);
		_upDownNavigationTime = null;
	}

	function getTotalNumberOfChannels(treeNavigator)
	{
		if(!treeNavigator) return 0;
		
		var startPosition = treeNavigator.Clone();
		if(!startPosition.MoveToFirstMedia()) return 0;

		var endPosition = treeNavigator.Clone();
		if(!endPosition.MoveToLastMedia()) return 0;

		var numberOfChannels = startPosition.CalcDistance(endPosition) + 1;
		return numberOfChannels;
	}

	this.moveToNextTree = function()
	{
		if(!_libraryNavigator.MoveToNextTree())
			return false;

		this.updateMediaTree(_libraryNavigator);
		return true;
	}

	this.moveToPreviousTree = function()
	{
		if(!_libraryNavigator.MoveToPreviousTree())
			return false;

		this.updateMediaTree(_libraryNavigator);
		return true;
	}

	this.moveToFirstChannel = function()
	{
		if(this.isEmpty()) return;

		var lastEvent = this.getCurrentEvent();
		_firstChannelIndex = 0;
		_currentChannelIndex = 0;
		this.go(_startTime);
		adjustCurentEvent(lastEvent);
	}

	this.moveToLastChannel = function()
	{
		if(this.isEmpty()) return;

		var lastEvent = this.getCurrentEvent();
		_firstChannelIndex = _totalNumberOfChannels - _numberOfChannels;
		_currentChannelIndex = _channels.length - 1;
		this.go(_startTime);
		adjustCurentEvent(lastEvent);
	}

	this.pageUp = function()
	{
		if(this.isEmpty()) return;

		var lastEvent = this.getCurrentEvent();

		_firstChannelIndex -= _numberOfChannels;
		if(_firstChannelIndex < 0)
			_firstChannelIndex = 0;

		this.go(_startTime);
		adjustCurentEvent(lastEvent);
	}

	this.pageDown = function()
	{
		if(this.isEmpty()) return;

		var lastEvent = this.getCurrentEvent();

		_firstChannelIndex += _numberOfChannels;
		if(_firstChannelIndex >= (_totalNumberOfChannels - _numberOfChannels))
			_firstChannelIndex = _totalNumberOfChannels - _numberOfChannels;

		this.go(_startTime);
		adjustCurentEvent(lastEvent);
	}

	this.moveToPreviousChannel = function()
	{
		if(this.isEmpty()) return;

		var lastEvent = this.getCurrentEvent();

		_currentChannelIndex--;
		if(_currentChannelIndex < 0)
		{
			_firstChannelIndex -= _numberOfChannels;

			_currentChannelIndex = _channels.length - 1;
			if(_firstChannelIndex < 0)
			{
				_currentChannelIndex += _firstChannelIndex;
				_firstChannelIndex = 0;
			}

			this.go(_startTime);
		}

		adjustCurentEvent(lastEvent);
	}

	this.moveToNextChannel = function()
	{
		if(this.isEmpty()) return;

		var lastEvent = this.getCurrentEvent();

		_currentChannelIndex++;
		if(_currentChannelIndex >= _channels.length)
		{
			_firstChannelIndex += _numberOfChannels;

			_currentChannelIndex = 0;
			if(_firstChannelIndex >= (_totalNumberOfChannels - _numberOfChannels))
			{
				_currentChannelIndex = _numberOfChannels + (_firstChannelIndex - _totalNumberOfChannels);
				_firstChannelIndex = _totalNumberOfChannels - _numberOfChannels;
			}
			this.go(_startTime);
		}

		adjustCurentEvent(lastEvent);
	}

	function adjustCurentEvent(lastEvent)
	{
		var channelInfo = _channels[_currentChannelIndex];
		var events = channelInfo.events;
		_currentEventIndex = 0;
		for(var i = 1; i < events.length; i++)
		{
			if(events[i].cutStartTime > _upDownNavigationTime)
				break;

			_currentEventIndex = i;
		}
		var currentEvent = _this.getCurrentEvent();
		fireCurrentEventChange(lastEvent, currentEvent);
	}

	this.moveBack = function()
	{
		if(this.isEmpty()) return;

		var channelInfo = this.getCurrentChannel();
		if(!channelInfo) return;

		var lastEvent = this.getCurrentEvent();
		var currentEvent = null;

		_currentEventIndex--;
		if(_currentEventIndex < 0)
		{
			var startTime = lastEvent.startTime;
			var s = startTime.valueOf() % _timeLineStep.valueOf();
			if(s > 0) s = _timeLineStep.valueOf() - s;
			startTime = startTime.add(s).subtract(_timeLinePeriod);
			_currentEventIndex = 1000;
			this.go(startTime);
			currentEvent = this.getCurrentEvent();
		}
		else
		{
			currentEvent = this.getCurrentEvent();
			fireCurrentEventChange(lastEvent, currentEvent);
		}
		updateUpDownNavigationTime(currentEvent);
	}

	this.moveForward = function()
	{
		if(this.isEmpty()) return;

		var channelInfo = this.getCurrentChannel();
		if(!channelInfo) return;

		var lastEvent = this.getCurrentEvent();
		var currentEvent = null;

		_currentEventIndex++;
		if(_currentEventIndex >= channelInfo.events.length)
		{
			var startTime = lastEvent.endTime;
			var s = (startTime.valueOf() % _timeLineStep.valueOf());
			startTime = startTime.subtract(s);
			_currentEventIndex = -1;
			this.go(startTime);
			currentEvent = this.getCurrentEvent();
		}
		else
		{
			currentEvent = this.getCurrentEvent();
			fireCurrentEventChange(lastEvent, currentEvent);
		}
		updateUpDownNavigationTime(currentEvent);
	}

	this.pageBack = function()
	{
		if(this.isEmpty()) return;

		var startTime = _startTime.subtract(_timeLineStep);
		this.go(startTime);
	}

	this.pageForward = function()
	{
		if(this.isEmpty()) return;

		var startTime = _startTime.add(_timeLineStep);
		this.go(startTime);
	}

	this.now = function()
	{
		if(this.isEmpty()) return;

		if(!moveToCurrentChannel())
		{
			_firstChannelIndex = 0;
			_currentChannelIndex = 0;
		}
		
		var startTime = new Date();
		startTime = startTime.subtract(_timeLineStep.valueOf() + (startTime.valueOf() % _timeLineStep.valueOf()));

		this.go(startTime);
		_upDownNavigationTime = new Date();
		adjustCurentEvent(null);
	}

	this.go = function(startTime)
	{
		if(this.isEmpty()) return;

		var lastEvent = this.getCurrentEvent();
		_startTime = startTime;
		_endTime = _startTime.add(_timeLinePeriod);

		var numberOfChannels = Math.min(maxNumberOfChannelsPerScreen, _totalNumberOfChannels);
		_channels = getNextChannels(_startTime, _endTime, _firstChannelIndex, numberOfChannels);
		_numberOfChannels = _channels.length;

		if(_channels.length == 0)
		{
			_currentChannelIndex = -1;
			_currentEventIndex = -1;
			_firstChannelIndex = -1;
			return;
		}

		if(_firstChannelIndex < 0)
			_firstChannelIndex = 0;

		if(_currentChannelIndex < 0)
			_currentChannelIndex = _channels.length > 0 ? 0 : -1;
		else if(_currentChannelIndex >= _channels.length)
			_currentChannelIndex = _channels.length - 1;

		var channelInfo = _channels[_currentChannelIndex]
		var events = channelInfo.events;
		if(_currentEventIndex < 0)
			_currentEventIndex = (events[0].noStart) ? 1 : 0;
		else if(_currentEventIndex >= events.length)
		{
			var lastEvent = events[events.length - 1];
			_currentEventIndex = lastEvent.noEnd ? events.length - 2 : events.length - 1;
			if(_currentEventIndex < 0)
				_currentEventIndex = 0;
		}
		firePageChange();

		var currentEvent = this.getCurrentEvent();
		fireCurrentEventChange(null, currentEvent);
	}

	function moveToCurrentChannel()
	{
		if(!_treeNavigator) return false;

		var currentTreeNavigator = _mediaController.getCurrentTreeNavigator();
		if(!currentTreeNavigator || !currentTreeNavigator.CurrentMedia) return false;

		if(!_treeNavigator.MoveTo(currentTreeNavigator)) return false; // If the trees are different return false.

		var startPosition = _treeNavigator.Clone();
		startPosition.MoveToFirstMedia();
		var currentChannelIndexGlobal = startPosition.CalcDistance(_treeNavigator);

		var numberOfChannelsAbove = Math.floor((_numberOfChannels - 1) / 2);
		var numberOfChannelsBelow = _numberOfChannels - 1 - numberOfChannelsAbove;

		// Make sure under the current channel threre is enough space, otherwise we need to move the current position down.
		var treeNavigator = _treeNavigator.Clone();
		for(var i = 0; i < numberOfChannelsBelow; i++)
		{
			if(!treeNavigator.MoveToNextMedia())
			{
				numberOfChannelsBelow = i;
				numberOfChannelsAbove = _numberOfChannels - 1 - numberOfChannelsBelow;
				break;
			}
		}

		_firstChannelIndex = Math.max(0, currentChannelIndexGlobal - numberOfChannelsAbove);
		_currentChannelIndex = currentChannelIndexGlobal - _firstChannelIndex;
		return true;
	}

	function getNextChannels(startTime, endTime, firstChannelIndex, numberOfChannels)
	{
		var channels = [];
		if(!_treeNavigator) return channels;
		
		var treeNavigator = _treeNavigator.Clone();
		if(treeNavigator.MoveToFirstMedia())
		{
			if(firstChannelIndex > 0)
			{
				if(!treeNavigator.MoveToMedia(firstChannelIndex))
					treeNavigator.MoveToFirstMedia();
			}
			
			for(var i = 0; i < numberOfChannels; i++)
			{
				var channelInfo = getChannelInfo(treeNavigator, startTime, endTime);
				channels.push(channelInfo);

				if(!treeNavigator.MoveToNextMedia())
					break;
			}
		}
		return channels;
	}

	function getChannelInfo(treeNavigator, startTime, endTime)
	{
		var channel = _mediaController.getMediaWrapper(treeNavigator.CurrentMedia);
		if(!channel) return null;
		
		var channelInfo = new Epg.ChannelInfo(channel);

		var folder = treeNavigator.CurrentFolder;
		if(folder)
		{
			channelInfo.folderName = folder.Name;

			var tree = folder.Tree;
			channelInfo.treeName = tree.Name;

			var lib = tree.Library;
			channelInfo.libraryName = lib.Name;
		}

		var epg = channel.getEpgForPeriod(startTime, endTime);
		for(var i = 0; i < epg.events.length; i++)
			epg.events[i].id = createEventCutId(channelInfo.uri, epg.events[i].startTime);

		channelInfo.events = cutChannelEvents(channelInfo.uri, epg.events, startTime, endTime);

		return channelInfo;
	}

	function cutChannelEvents(channelId, epgEvents, startTime, endTime)
	{
		var ms = endTime.valueOf() - startTime.valueOf();
		var eventCuts = [];
		var lastEventEndTime = startTime;
		for(var i = 0; i < epgEvents.length; i++)
		{
			var epgEvent = epgEvents[i];
			var nextEpgEvent = (i < epgEvents.length - 1) ? epgEvents[i + 1] : null;

			if(epgEvent.startTime.valueOf() == epgEvent.endTime.valueOf()) continue;

			var eventCutStartTime = Date.max(epgEvent.startTime, startTime);
			var eventCutEndTime = Date.min(epgEvent.endTime, endTime);
			if(nextEpgEvent)
			{
				var nextEpgCutStartTime = Date.max(nextEpgEvent.startTime, startTime);
				eventCutEndTime = Date.min(eventCutEndTime, nextEpgCutStartTime);
			}

			if(eventCutStartTime.valueOf() == eventCutEndTime.valueOf()) continue;

			if(eventCutStartTime > lastEventEndTime.add(_minNoDataPeriod))
				eventCuts.push(createNoDataEvent(channelId, lastEventEndTime, eventCutStartTime));

			var eventCut = {};
			eventCut.id = createEventCutId(channelId, eventCutStartTime);
			eventCut.noStart = epgEvent.startTime < startTime;
			eventCut.noEnd = epgEvent.endTime > endTime;
			eventCut.startTime = epgEvent.startTime;
			eventCut.endTime = epgEvent.endTime;
			eventCut.cutStartTime = eventCutStartTime;
			eventCut.cutEndTime = eventCutEndTime;
			eventCut.name = epgEvent.name;
			eventCut.description = epgEvent.description;

			eventCuts.push(eventCut);
			lastEventEndTime = eventCutEndTime;
		}
		if(epgEvents.length == 0 || endTime > lastEventEndTime.add(_minNoDataPeriod))
			eventCuts.push(createNoDataEvent(channelId, lastEventEndTime, endTime));

		return eventCuts;
	}

	function createNoDataEvent(channelId, startTime, endTime)
	{
		var noDataEvent = {};
		noDataEvent.id = createEventCutId(channelId, startTime);
		noDataEvent.noStart = false;
		noDataEvent.noEnd = false;
		noDataEvent.noData = true;
		noDataEvent.startTime = startTime;
		noDataEvent.endTime = endTime;
		noDataEvent.cutStartTime = noDataEvent.startTime;
		noDataEvent.cutEndTime = noDataEvent.endTime;
		noDataEvent.name = "No data available";
		return noDataEvent;
	}

	function createEventCutId(channelUri, eventStartTime)
	{
		return channelUri.replace(":", "-") + "-" + Math.round((eventStartTime.valueOf() / 1000));
	}

	function updateUpDownNavigationTime(e)
	{
		_upDownNavigationTime = !e ? null : 
			e.cutStartTime.add(Math.round(e.cutEndTime.subtract(e.cutStartTime).valueOf() / 2));
	}

	function firePageChange()
	{
		_this.onPageChange.fire(_this, null);
	}

	function fireCurrentEventChange(lastEvent, currentEvent)
	{
		var e = window.top.document.createEventObject();
		e.lastEvent = lastEvent;
		e.currentEvent = currentEvent;
		_this.onEventChange.fire(_this, e);
	}

	initialize();
}
Type.registerType('Epg');


Epg.ChannelInfo = function(_channel)
{
	this.uri = _channel.uri;
	this.name = _channel.name;
	this.number = _channel.number;
	this.libraryName = null;
	this.treeName = null;
	this.folderName = null;
	this.events = [];
}
Type.registerType('Epg.ChannelInfo');
